package com.tolerate.managespringboot.controller.common;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tolerate.managespringboot.entity.FileEntity;
import com.tolerate.managespringboot.exception.BusinessException;
import com.tolerate.managespringboot.service.FileEntityService;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.DecimalFormat;

@RestController
@RequestMapping("file")
public class FileController {

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @Autowired
    private FileEntityService fileEntityService;

    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        // 获取文件名
        String originalFilename = file.getOriginalFilename();
        // 获取文件扩展名
        String contentType = StrUtil.subAfter(originalFilename, ".", true);
        // 获取文件大小,单位字节 (B) 换算单位：1KB=1024B 1MB=1024KB 1GB=1024MB
        long size = file.getSize();
        // 文件大小转换
        String sizeStr = convertFileSize(size);

        String uuid = IdUtil.fastSimpleUUID();
        // 重命名文件
        String fileName = uuid + StrUtil.DOT + contentType;//UUID.jpg

        // 创建一个目录对象
        File dir = new File(fileUploadPath);
        // 判断当前目录是否存在
        if (!dir.exists()) {
            // 目录不存在，需要创建
            dir.mkdirs();
        }

        // 判断文件是否已存在
        // 避免上传重复文件
        String md5 = DigestUtils.md5Hex(file.getBytes());
        LambdaQueryWrapper<FileEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FileEntity::getFileMd5, md5);
        String url;
        if (fileEntityService.list(lambdaQueryWrapper).size() > 0) {
            // 文件已存在
            url = fileEntityService.list(lambdaQueryWrapper).get(0).getUrl();
        } else {
            // 文件不存在
            try {
                // 将临时文件转存到指定位置
                file.transferTo(new File(fileUploadPath + fileName));// 将文件保存的位置
            } catch (IOException e) {
                new BusinessException(405, "文件上传失败");
            }
            // 文件URL
            url = "http://localhost:9090/file/" + fileName;
        }

        // 信息保存到数据库中
        FileEntity fileEntity = new FileEntity();
        fileEntity.setName(originalFilename);
        fileEntity.setSize(sizeStr);
        fileEntity.setType(contentType);
        fileEntity.setUrl(url);
        fileEntity.setFileMd5(md5);
        fileEntityService.save(fileEntity);
        return url;
    }

    /**
     * 文件下载
     *
     * @param fileUUID 文件的UUID
     * @param response
     * @throws IOException
     */
    @GetMapping("/{fileUUID}")
    public void downLoad(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {
        // 输入流，通过输入流读取文件内容
        FileInputStream fileInputStream = new FileInputStream(new File(fileUploadPath + fileUUID));
        // response.getWriter() 是字符流，只能输出文本数据
        // response.getOutputStream() 是字节流，可以传输任意数据
        ServletOutputStream outputStream = response.getOutputStream();

//      这段代码用于设置HTTP响应头，实现文件下载功能。
//      response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUId, "UTF-8"))；
//      功能：设置响应头的Content-Disposition字段为attachment，并指定下载时的文件名。
//      解释：addHeader方法用于向响应头中添加新的头信息。这里设置的是Content-Disposition字段，其值为attachment，表示以附件形式下载文件。filename参数通过URLEncoder.encode方法进行URL编码，确保文件名中的特殊字符或非ASCII字符能够正确传输。
//      response.setContentType("application/octet-stream");
//      功能：设置响应的内容类型为二进制流。
//      解释：setContentType方法用于指定HTTP响应的内容类型。在这里，设置为application/octet-stream，表示响应的内容是以二进制流的形式传输，这是下载文件时常用的内容类型，尤其是当文件类型不确定时。
        // 设置输出流格式
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));
        response.setContentType("application/octet-stream");

        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fileInputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, len);// 从bytes数组中0开始写len 结束
            outputStream.flush();// 不刷新没数据
        }
        // 关闭资源
        outputStream.close();
        fileInputStream.close();

    }


    private static String convertFileSize(long size) {
        if (size <= 0) return "0";
        final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
        int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
        return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    }

}

